package service;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.ejb.EJB;
import javax.ejb.LocalBean;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Join;
import javax.persistence.criteria.Root;

import dto.PercentualRequisitoDTO;

import entity.Atividade;
import entity.CasoUso;
import entity.Fase;
import entity.Iteracao;
import entity.Projeto;
import entity.Recurso;
import entity.Requisito;
import entity.StatusAtividade;
import entity.Fase.FaseRUP;
import exception.AlmiranteException;



@Stateless
@LocalBean
public class RequisitoService {
	@PersistenceContext(unitName = "almirante")
	private EntityManager em;
	
	@EJB
	private ProjetoService projetoService;
	
	public List<Requisito> listarRequisitosDoProjeto(Long idProjeto) {
		CriteriaBuilder builder = em.getCriteriaBuilder();
		CriteriaQuery<Requisito> criteria = builder.createQuery(Requisito.class);
		Root<Requisito> requisito = criteria.from(Requisito.class);
		Join<Requisito, Projeto> projeto = requisito.join("projeto"); 
		
		criteria.distinct(true)
			.where(builder.equal(projeto.get("id"), idProjeto))
			.orderBy(builder.desc(requisito.get("prioridade")));
		
		return em.createQuery(criteria).getResultList();
	}
	
	public void incluirRequisito(Long idProjeto, Requisito requisito) {
		Projeto projeto = projetoService.obterProjeto(idProjeto);
		
		projeto.adicionarRequisito(requisito);
		em.persist(requisito);
	}
	
	public Requisito obterRequisito(Long idRequisito) {
		return em.find(Requisito.class, idRequisito);
	}
	
	public void excluirRequisito(Long idRequisito) {
		Requisito requisito = obterRequisito(idRequisito);
		Projeto projeto = requisito.getProjeto();
		if (!projeto.getIteracoes().isEmpty()) {
			for (Iteracao iteracao : projeto.getIteracoes()) {
				if (iteracao.possuiRequisito(requisito)) {
					throw new AlmiranteException("Requisito não pode ser excluído, pois está vinculado à uma iteração.");
				}
			}
		}
		
		em.remove(requisito);
	}
	 
	
	public void alterarRequisito(Requisito requisito) {
		em.merge(requisito);
	}
	
	public List<Requisito> listarRequisitosDaIteracao(Long idIteracao) {
		String jpql = "SELECT r " +
				"FROM Requisito r, Iteracao i " +
				"WHERE r MEMBER OF i.requisitos " +
				"AND i.id = :idIteracao ";
		
		return em.createQuery(jpql, Requisito.class)
				.setParameter("idIteracao", idIteracao).getResultList();
	}
	
	public List<Requisito> listarRequisitosNaoVinculadosAIteracao(Long idIteracao) {
		String jpql = "SELECT r " +
				"FROM Requisito r, Iteracao i " +
				"WHERE r NOT MEMBER OF i.requisitos " +
				"AND i.id = :idIteracao " +
				"AND r.projeto.id = i.fase.projeto.id";
		
		return em.createQuery(jpql, Requisito.class)
				.setParameter("idIteracao", idIteracao).getResultList();
	}
	
	public List<Requisito> listarRequisitosDaAtividade(Long idAtividade) {
		String jpql = "SELECT r " +
				"FROM Requisito r, Atividade a " +
				"WHERE r MEMBER OF a.requisitos " +
				"AND a.id = :idAtividade ";
		
		return em.createQuery(jpql, Requisito.class)
				.setParameter("idAtividade", idAtividade).getResultList();
	}
	
	public List<Requisito> listarRequisitosNaoVinculadosAAtividade(Long idAtividade) {
		String jpql = "SELECT r " +
				"FROM Requisito r, Atividade a " +
				"WHERE r NOT MEMBER OF a.requisitos " +
				"AND a.id = :idAtividade " +
				"AND r MEMBER OF a.iteracao.requisitos ";
		
		return em.createQuery(jpql, Requisito.class)
				.setParameter("idAtividade", idAtividade).getResultList();
	}
	
	public List<PercentualRequisitoDTO> obterPercentagemRequisito(Requisito requisito){
		String query = "SELECT a FROM Atividade a LEFT JOIN a.requisitos req WHERE req.id = :idRequisito";
		
		List<Atividade> atividades = em.createQuery(query).setParameter("idRequisito", requisito.getId()).getResultList();

		return calcularPercentagem(atividades);
	}
	
	public List<PercentualRequisitoDTO> obterPercentagemCasoUso(CasoUso casoUso){
		String query = "SELECT a FROM Atividade a LEFT JOIN a.casosUso uc WHERE uc.id = :idCasoUso";
		
		List<Atividade> atividades = em.createQuery(query).setParameter("idCasoUso", casoUso.getId()).getResultList();

		return calcularPercentagem(atividades);
	}
	
	
	
	public List<PercentualRequisitoDTO> calcularPercentagem(List<Atividade> atividades){
		

		Map<FaseRUP, Integer> quantidadeOcorrencias = new HashMap<Fase.FaseRUP, Integer>(); 
		Map<FaseRUP, Integer> quantidadeConcluidos = new HashMap<Fase.FaseRUP, Integer>();
		
		Map<FaseRUP, Integer> percentual = new HashMap<Fase.FaseRUP, Integer>();
		List<PercentualRequisitoDTO> percentualT = new ArrayList<PercentualRequisitoDTO>();
		
		
		for (Atividade atividade : atividades) {
			FaseRUP faseRup = atividade.getIteracao().getFase().getFaseRup();
			
			if(quantidadeOcorrencias.containsKey(faseRup)){
				
				Integer quantidade = quantidadeOcorrencias.get(faseRup).intValue();
				quantidadeOcorrencias.put(faseRup, ++quantidade);
			}
			else {
				quantidadeOcorrencias.put(faseRup, 1);
			}
			
			if (atividade.getStatus().equals(StatusAtividade.FINALIZADA)){
				if(quantidadeConcluidos.containsKey(faseRup)){
					
					Integer quantidade = quantidadeConcluidos.get(faseRup).intValue();
					quantidadeConcluidos.put(faseRup, ++quantidade);
				}
				else {
					quantidadeConcluidos.put(faseRup, 1);
				}
			}
			
		}
		
		for (Map.Entry<FaseRUP, Integer> entry : quantidadeOcorrencias.entrySet()) {
			FaseRUP faseRUP = entry.getKey();
			if (quantidadeConcluidos.containsKey(faseRUP)){
				percentual.put(faseRUP, quantidadeConcluidos.get(faseRUP)*100/entry.getValue());
				percentualT.add(new PercentualRequisitoDTO(faseRUP.getNome(),quantidadeConcluidos.get(faseRUP)*100/entry.getValue()));
			}
			else {
				percentual.put(faseRUP,0);
				percentualT.add(new PercentualRequisitoDTO(faseRUP.getNome(), 0));
			}
			
		}
		
		return percentualT;
	}
	
	//Métodos para tratar casos de uso
	
	public void incluirCasoUso(Long idProjeto, CasoUso casoUso) {
		Projeto projeto = projetoService.obterProjeto(idProjeto);
		
		projeto.adicionarCasoUso(casoUso);
		em.persist(casoUso);
	}
	
	public List<CasoUso> listarCasoUsoDoProjeto(Long idProjeto) {
		Query query = em.createNamedQuery("listarCasoUsoPorProjeto",CasoUso.class);
		query.setParameter("idProjeto", idProjeto);
		
		return query.getResultList();
	}
	
	public CasoUso obterCasoUso(Integer idCasoUso) {
		CasoUso retorno = em.find(CasoUso.class, idCasoUso);
		return retorno;
	}
	
	public void alterarCasoUso(CasoUso casoUso) {
		
		em.merge(casoUso);
	}
	
	public void excluirCasoUso(CasoUso casoUso) {
		//So pode esxcluir casos de uso que não tenham tarefas cadastradas
		
		
		String hql = "SELECT i FROM Iteracao i LEFT JOIN i.casosUso uc WHERE uc.id = :idUc";
		Query query = em.createQuery(hql);
		query.setParameter("idUc", casoUso.getId());
		List<Iteracao> listCasoUsoRelacionados = query.getResultList();
		
		if (listCasoUsoRelacionados.size() <= 0){
			casoUso = em.find(CasoUso.class, casoUso.getId());
			em.remove(casoUso);
		}
		else {
			throw new AlmiranteException("Não é possível deletar Caso de Uso pois já está vínculado a uma iteração");
		}
		
	}
	
	public List<CasoUso> listarCasosUsoNaoVinculadosAIteracao(Long idIteracao) {
		String jpql = "SELECT c " +
				"FROM CasoUso c, Iteracao i " +
				"WHERE c NOT MEMBER OF i.casosUso " +
				"AND i.id = :idIteracao " +
				"AND c.projeto.id = i.fase.projeto.id";
		
		return em.createQuery(jpql, CasoUso.class)
				.setParameter("idIteracao", idIteracao).getResultList();
	}
	
	public List<CasoUso> listarCasosUsoDaIteracao(Long idIteracao) {
		String jpql = "SELECT c " +
				"FROM CasoUso c, Iteracao i " +
				"WHERE c MEMBER OF i.casosUso " +
				"AND i.id = :idIteracao ";
		
		return em.createQuery(jpql, CasoUso.class)
				.setParameter("idIteracao", idIteracao).getResultList();
	}
	

	public List<CasoUso> listarCasosUsoDaAtividade(Long idAtividade) {
		String jpql = "SELECT c " +
				"FROM CasoUso c, Atividade a " +
				"WHERE c MEMBER OF a.casosUso " +
				"AND a.id = :idAtividade ";
		
		return em.createQuery(jpql, CasoUso.class)
				.setParameter("idAtividade", idAtividade).getResultList();
	}
	
	public List<CasoUso> listarCasosUsoNaoVinculadosAAtividade(Long idAtividade) {
		String jpql = "SELECT c " +
				"FROM CasoUso c, Atividade a " +
				"WHERE c NOT MEMBER OF a.casosUso " +
				"AND a.id = :idAtividade " +
				"AND c MEMBER OF a.iteracao.casosUso ";
		
		return em.createQuery(jpql, CasoUso.class)
				.setParameter("idAtividade", idAtividade).getResultList();
	}
}
